﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace FolderSynchronizer
{
    /// <summary>
    /// Класс, для игнорирования файлов в исходном каталоге.
    /// </summary>
    public class Ignorer
    {
        private readonly List<DirectoryInfo> tempExludeFolders;
        private readonly List<DirectoryInfo> targetDirInfo; 
        private readonly List<FileInfo> filesFromSubFoldersInExcludeFolders;
        private readonly List<DirectoryInfo> foldersFromExcludeFolders;
        private readonly DirectoryInfo targetDir;
        private readonly List<FileInfo> targetPathFiles;
        private readonly string[] excludeFolders;

        /// <summary>
        /// Конструктор.
        /// </summary>
        /// <param name="targetPath">Путь к конечному каталогу.</param>
        /// <param name="excludeFolders">Массив относительных путей к папкам, которые нельзя обновлять.</param>
        public Ignorer(string targetPath, string[] excludeFolders)
        {
            this.excludeFolders = excludeFolders;
            filesFromSubFoldersInExcludeFolders = new List<FileInfo>();
            tempExludeFolders = new List<DirectoryInfo>();
            foldersFromExcludeFolders = new List<DirectoryInfo>();
            targetDir = new DirectoryInfo(targetPath);
            targetPathFiles = targetDir.GetFiles(".", SearchOption.AllDirectories).ToList();
            targetDirInfo = targetDir.GetDirectories(".", SearchOption.AllDirectories).ToList();
            Compute();
        }

        /// <summary>
        /// Получить список файлов, после игнорирования.
        /// </summary>
        /// <returns>Список типа FileInfo.</returns>
        public List<FileInfo> GetFiles()
        {
            return targetPathFiles;
        }

        /// <summary>
        /// Получить список каталогов, после игнорирования.
        /// </summary>
        /// <returns>Список типа DirectoryInfo.</returns>
        public List<DirectoryInfo> GetDirectories()
        {
            return targetDirInfo;
        }

        private void Compute()
        {
            DeleteExludeFoldersFromList();
            ExctractIgnorFilesAndFolders();
            DeleteIgnoreFilesFromList();
            DeleteIgnoreFolderFromList();
        }


        private void DeleteExludeFoldersFromList()
        {
            if (excludeFolders == null) return;
            foreach (string name in excludeFolders)
            {
                for (int i = 0; i < targetDirInfo.Count; i++)
                {
                    string ralativeFolderName = targetDirInfo[i].FullName.Substring(targetDir.FullName.IndexOf(targetDir.Name, System.StringComparison.Ordinal) + targetDir.Name.Count() + 1);
                    if (ralativeFolderName == name)
                    {
                        tempExludeFolders.Add(targetDirInfo[i]);
                        targetDirInfo.RemoveAt(i);
                    }
                }
            }
        }

        private void ExctractIgnorFilesAndFolders()
        {
            foreach (DirectoryInfo excludeFolder in tempExludeFolders)
            {
                filesFromSubFoldersInExcludeFolders.AddRange(excludeFolder.GetFiles(".", SearchOption.AllDirectories).ToList());
                foldersFromExcludeFolders.AddRange(excludeFolder.GetDirectories(".", SearchOption.AllDirectories).ToList());
            }
        }

        private void DeleteIgnoreFilesFromList()
        {
            foreach (FileInfo file in filesFromSubFoldersInExcludeFolders)
            {
                FileInfo fileInTarget = targetPathFiles.Find(p => p.FullName == file.FullName);

                if (fileInTarget != null)
                {
                    targetPathFiles.Remove(fileInTarget);
                }
            }
        }

        private void DeleteIgnoreFolderFromList()
        {
            foreach (DirectoryInfo folder in foldersFromExcludeFolders)
            {
                DirectoryInfo folderInTarget = targetDirInfo.Find(p => p.FullName == folder.FullName);

                if (folderInTarget != null)
                {
                    this.targetDirInfo.Remove(folderInTarget);
                }
            }
        }
    }
}
